use std::{num::ParseFloatError, num::ParseIntError, path::PathBuf, str::FromStr};
use std::fmt::{self, Display};
use std::error::Error;

use anyhow::{bail, Context, Result};
use clap::{ArgAction, Parser, Subcommand, ValueHint};
use rusqlite::{params, Connection};
use comfy_table::{Table, Cell, Color};


const FULL_LEVEL: i64 = 3;
const FULL_EXP:   f64 = 10_000_000.0;

const BODY_SIM_KEY_PADDING: usize = 5;
const BODY_SIM_VALUE_PADDING: usize = 10;
const DOUBLE_PROPERTY: &[u8] = b"DoubleProperty";

// 目标属性值。与 Python 的 SET_ATTRIBUTES 对齐
const TARGET_ATTRS: &[(&[u8], f64)] = &[
    (b"BaseStrength",     8.0), // 1.0..=8.0
    (b"BaseConstitution", 5.0), // 1.0..=5.0
    (b"BaseDexterity",    5.0), // 1.0..=5.0
    (b"BaseIntelligence", 8.0), // 1.0..=8.0
];


#[derive(Parser, Debug)]
#[command(name = "scum-skill-edit")]
#[command(about = "一键满配，或设置任意技能与属性值")]
#[command(
    after_help = "示例:\n  scum-skill-edit ./SCUM.db UserName set -s RiflesSkill,3 -a BaseStrength,8"
)]
struct Cli {
    /// SCUM.db 的路径（位置参数1）
    #[arg(value_hint = ValueHint::FilePath)]
    db: PathBuf,

    /// 玩家名字
    player: String,

    /// 子命令
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand, Debug)]
enum Commands {
    /// 一键满配（示例）
    Full,

    /// 显示技能与属性
    Show,

    /// 自定义技能与属性
    Set(SetCommand),
}

#[derive(Parser, Debug)]
struct SetCommand {
    /// 形如: -s RiflesSkill,3  可重复
    #[arg(
        short = 's',
        long = "skill",
        value_name = "NAME,LEVEL",
        value_parser = SkillKV::from_str,
        action = ArgAction::Append
    )]
    skills: Vec<SkillKV>,

    /// 形如: -a BaseStrength,8  可重复
    #[arg(
        short = 'a',
        long = "attribute",
        value_name = "NAME,VALUE",
        value_parser = AttrKV::from_str,
        action = ArgAction::Append
    )]
    attributes: Vec<AttrKV>,
}

/* ---------------- 受限的枚举白名单 ---------------- */

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SkillName {
    BoxingSkill,
    AwarenessSkill,
    RiflesSkill,
    SnipingSkill,
    CamouflageSkill,
    SurvivalSkill,
    MeleeWeaponsSkill,
    HandgunSkill,
    RunningSkill,
    EnduranceSkill,
    TacticsSkill,
    CookingSkill,
    ThieverySkill,
    ArcherySkill,
    DrivingSkill,
    EngineeringSkill,
    DemolitionSkill,
    MedicalSkill,
    MotorcycleSkill,
    StealthSkill,
    AviationSkill,
    ResistanceSkill,
    FarmingSkill,
}

impl Display for SkillName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(match self {
            SkillName::BoxingSkill => "BoxingSkill",
            SkillName::AwarenessSkill => "AwarenessSkill",
            SkillName::RiflesSkill => "RiflesSkill",
            SkillName::SnipingSkill => "SnipingSkill",
            SkillName::CamouflageSkill => "CamouflageSkill",
            SkillName::SurvivalSkill => "SurvivalSkill",
            SkillName::MeleeWeaponsSkill => "MeleeWeaponsSkill",
            SkillName::HandgunSkill => "HandgunSkill",
            SkillName::RunningSkill => "RunningSkill",
            SkillName::EnduranceSkill => "EnduranceSkill",
            SkillName::TacticsSkill => "TacticsSkill",
            SkillName::CookingSkill => "CookingSkill",
            SkillName::ThieverySkill => "ThieverySkill",
            SkillName::ArcherySkill => "ArcherySkill",
            SkillName::DrivingSkill => "DrivingSkill",
            SkillName::EngineeringSkill => "EngineeringSkill",
            SkillName::DemolitionSkill => "DemolitionSkill",
            SkillName::MedicalSkill => "MedicalSkill",
            SkillName::MotorcycleSkill => "MotorcycleSkill",
            SkillName::StealthSkill => "StealthSkill",
            SkillName::AviationSkill => "AviationSkill",
            SkillName::ResistanceSkill => "ResistanceSkill",
            SkillName::FarmingSkill => "FarmingSkill",
        })
    }
}

impl FromStr for SkillName {
    type Err = SkillParseError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use SkillName::*;
        match s.trim() {
            "BoxingSkill" => Ok(BoxingSkill),
            "AwarenessSkill" => Ok(AwarenessSkill),
            "RiflesSkill" => Ok(RiflesSkill),
            "SnipingSkill" => Ok(SnipingSkill),
            "CamouflageSkill" => Ok(CamouflageSkill),
            "SurvivalSkill" => Ok(SurvivalSkill),
            "MeleeWeaponsSkill" => Ok(MeleeWeaponsSkill),
            "HandgunSkill" => Ok(HandgunSkill),
            "RunningSkill" => Ok(RunningSkill),
            "EnduranceSkill" => Ok(EnduranceSkill),
            "TacticsSkill" => Ok(TacticsSkill),
            "CookingSkill" => Ok(CookingSkill),
            "ThieverySkill" => Ok(ThieverySkill),
            "ArcherySkill" => Ok(ArcherySkill),
            "DrivingSkill" => Ok(DrivingSkill),
            "EngineeringSkill" => Ok(EngineeringSkill),
            "DemolitionSkill" => Ok(DemolitionSkill),
            "MedicalSkill" => Ok(MedicalSkill),
            "MotorcycleSkill" => Ok(MotorcycleSkill),
            "StealthSkill" => Ok(StealthSkill),
            "AviationSkill" => Ok(AviationSkill),
            "ResistanceSkill" => Ok(ResistanceSkill),
            "FarmingSkill" => Ok(FarmingSkill),
            other => Err(SkillParseError::UnknownName(other.to_string())),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum AttrName {
    BaseStrength,      // 1.0..=8.0
    BaseConstitution,  // 1.0..=5.0
    BaseDexterity,     // 1.0..=5.0
    BaseIntelligence,  // 1.0..=8.0
}

impl Display for AttrName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(match self {
            AttrName::BaseStrength => "BaseStrength",
            AttrName::BaseConstitution => "BaseConstitution",
            AttrName::BaseDexterity => "BaseDexterity",
            AttrName::BaseIntelligence => "BaseIntelligence",
        })
    }
}

impl FromStr for AttrName {
    type Err = AttrParseError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use AttrName::*;
        match s.trim() {
            "BaseStrength" => Ok(BaseStrength),
            "BaseConstitution" => Ok(BaseConstitution),
            "BaseDexterity" => Ok(BaseDexterity),
            "BaseIntelligence" => Ok(BaseIntelligence),
            other => Err(AttrParseError::UnknownName(other.to_string())),
        }
    }
}

/* ---------------- KV 解析，含范围校验 ---------------- */

#[derive(Debug, Clone)]
struct SkillKV {
    pub name: SkillName,
    pub level: u32, // 0..=3
}
impl FromStr for SkillKV {
    type Err = SkillParseError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let (name_raw, val) = split_once_comma(s).ok_or(SkillParseError::Format)?;
        let name: SkillName = name_raw.parse()?; // 严格白名单
        let level: u32 = val.parse().map_err(SkillParseError::Level)?;
        if level > 3 {
            return Err(SkillParseError::OutOfRange(level));
        }
        Ok(SkillKV { name, level })
    }
}

#[derive(Debug, Clone)]
struct AttrKV {
    pub name: AttrName,
    pub value: f64,
}
impl FromStr for AttrKV {
    type Err = AttrParseError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let (name_raw, val) = split_once_comma(s).ok_or(AttrParseError::Format)?;
        let name: AttrName = name_raw.parse()?; // 严格白名单
        let value: f64 = val.parse().map_err(AttrParseError::Value)?;
        // 按属性名分别校验范围
        let ok = match name {
            AttrName::BaseStrength | AttrName::BaseIntelligence => (1.0..=8.0).contains(&value),
            AttrName::BaseConstitution | AttrName::BaseDexterity => (1.0..=5.0).contains(&value),
        };
        if !ok {
            return Err(AttrParseError::OutOfRange { name, got: value });
        }
        Ok(AttrKV { name, value })
    }
}

/* ---------------- 错误类型，带可选值提示 ---------------- */

#[derive(Debug)]
pub enum SkillParseError {
    Format,
    Level(ParseIntError),
    OutOfRange(u32),
    UnknownName(String),
}
impl Display for SkillParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            SkillParseError::Format =>
                write!(f, "skill 参数应为 NAME,LEVEL（例如 -s RiflesSkill,3）"),
            SkillParseError::Level(e) =>
                write!(f, "无法解析技能等级: {}", e),
            SkillParseError::OutOfRange(n) =>
                write!(f, "技能等级超出范围: {}（允许 0..=3）", n),
            SkillParseError::UnknownName(s) =>
                write!(f, "未知技能名: {}。可选值: {}", s, ALL_SKILL_NAMES.join(", ")),
        }
    }
}
impl Error for SkillParseError {}

#[derive(Debug)]
pub enum AttrParseError {
    Format,
    Value(ParseFloatError),
    OutOfRange { name: AttrName, got: f64 },
    UnknownName(String),
}
impl Display for AttrParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AttrParseError::Format =>
                write!(f, "attribute 参数应为 NAME,VALUE（例如 -a BaseStrength,8）"),
            AttrParseError::Value(e) =>
                write!(f, "无法解析属性值: {}", e),
            AttrParseError::OutOfRange { name, got } => {
                let range = match name {
                    AttrName::BaseStrength | AttrName::BaseIntelligence => "1.0..=8.0",
                    AttrName::BaseConstitution | AttrName::BaseDexterity => "1.0..=5.0",
                };
                write!(f, "属性 {} 的值超出范围: {}（允许 {}）", name, got, range)
            }
            AttrParseError::UnknownName(s) =>
                write!(f, "未知属性名: {}。可选值: {}", s, ALL_ATTR_NAMES.join(", ")),
        }
    }
}
impl Error for AttrParseError {}

/* ---------------- 帮助输出要用到的“可选值列表” ---------------- */

const ALL_SKILL_NAMES: &[&str] = &[
    "BoxingSkill","AwarenessSkill","RiflesSkill","SnipingSkill","CamouflageSkill",
    "SurvivalSkill","MeleeWeaponsSkill","HandgunSkill","RunningSkill","EnduranceSkill",
    "TacticsSkill","CookingSkill","ThieverySkill","ArcherySkill","DrivingSkill",
    "EngineeringSkill","DemolitionSkill","MedicalSkill","MotorcycleSkill","StealthSkill",
    "AviationSkill","ResistanceSkill","FarmingSkill",
];

const ALL_ATTR_NAMES: &[&str] = &[
    "BaseStrength","BaseConstitution","BaseDexterity","BaseIntelligence",
];

/* ---------------- 工具函数 ---------------- */

fn split_once_comma(s: &str) -> Option<(String, String)> {
    let mut it = s.splitn(2, ',');
    let k = it.next()?.trim();
    let v = it.next()?.trim();
    if k.is_empty() || v.is_empty() { return None; }
    Some((k.to_string(), v.to_string()))
}

/* ---------------- 程序入口与数据库 ---------------- */

fn main() -> Result<()> {
    let cli = Cli::parse();

    // 只有在成功解析参数后，才连接数据库
    let conn = open_conn(&cli.db).context("打开数据库失败")?;
    let prisoner_id = get_prisoner_id(&conn, &cli.player).context("获取囚犯ID失败")?;

    match cli.command {
        Commands::Full => {
            max_out_skills(&conn, prisoner_id)?;
            max_out_attributes(&conn, prisoner_id)?;
        }
        Commands::Show => {
            show_skills(&conn, prisoner_id)?;
            show_attributes(&conn, prisoner_id)?;
        }
        Commands::Set(set) => {
            if !set.skills.is_empty() {
                let count = set_skills(&conn, prisoner_id, &set.skills)?;
                println!("✓ 已更新 {} 个技能", count);
            }
            if !set.attributes.is_empty() {
                let count = set_attributes(&conn, prisoner_id, &set.attributes)?;
                println!("✓ 已更新 {} 个属性", count);
            }
        }
    }

    Ok(())
}

fn open_conn(db: &PathBuf) -> Result<Connection> {
    let conn = Connection::open(db)?;
    Ok(conn)
}

pub fn get_prisoner_id(conn: &Connection, player: &str) -> Result<i64> {
    // 先精确匹配
    let mut stmt = conn
        .prepare(
            r#"
            SELECT prisoner.id
            FROM prisoner
            LEFT JOIN user_profile ON prisoner.user_profile_id = user_profile.id
            WHERE user_profile.authority_name IS NULL
              AND user_profile.name = ?1
            ORDER BY prisoner.id ASC
            LIMIT 1
            "#,
        )
        .context("prepare get_prisoner_id statement failed")?;

    let exact: std::result::Result<i64, _> = stmt.query_row(params![player], |row| row.get(0));
    if let Ok(id) = exact {
        return Ok(id);
    }

    // 兜底：名字大小写不敏感匹配
    let mut stmt_fallback = conn
        .prepare(
            r#"
            SELECT prisoner.id
            FROM prisoner
            LEFT JOIN user_profile ON prisoner.user_profile_id = user_profile.id
            WHERE user_profile.authority_name IS NULL
              AND user_profile.name = ?1 COLLATE NOCASE
            ORDER BY prisoner.id ASC
            LIMIT 1
            "#,
        )
        .context("prepare fallback statement failed")?;

    let fallback: std::result::Result<i64, _> = stmt_fallback.query_row(params![player], |row| row.get(0));
    if let Ok(id) = fallback {
        return Ok(id);
    }

    Err(anyhow::anyhow!(
        "no local single-player prisoner found for name '{}'",
        player
    ))
}

fn set_skills(
    conn: &rusqlite::Connection,
    prisoner_id: i64,
    skills: &[SkillKV],
) -> anyhow::Result<usize> {
    use rusqlite::OptionalExtension;

    if skills.is_empty() {
        return Ok(0);
    }

    let mut affected = 0usize;

    for kv in skills {
        // 白名单你在 FromStr 已经卡死了，这里只需要转成字符串
        let name_str = kv.name.to_string();
        let level_i64 = kv.level as i64;

        // 先看看有没有这条技能
        let existing: Option<i64> = conn
            .query_row(
                "SELECT level FROM prisoner_skill WHERE prisoner_id = ?1 AND name = ?2",
                params![prisoner_id, &name_str],
                |r| r.get(0),
            )
            .optional()
            .context("exist-check query failed")?;

        match existing {
            Some(cur_level) => {
                // 只有变更才写，省 IO
                if cur_level != level_i64 {
                    let n = conn
                        .execute(
                            "UPDATE prisoner_skill SET level = ?1 WHERE prisoner_id = ?2 AND name = ?3",
                            params![level_i64, prisoner_id, &name_str],
                        )
                        .context("update skill failed")?;
                    affected += n;
                }
            }
            None => {
                // 没有就补一条，经验置 0.0，别替玩家瞎编数
                let n = conn
                    .execute(
                        "INSERT INTO prisoner_skill (prisoner_id, name, level, experience) VALUES (?1, ?2, ?3, ?4)",
                        params![prisoner_id, &name_str, level_i64, 0.0],
                    )
                    .context("insert skill failed")?;
                affected += n;
            }
        }
    }

    Ok(affected)
}

fn max_out_skills(conn: &rusqlite::Connection, prisoner_id: i64) -> anyhow::Result<usize> {
    // 先查询所有技能名并收集到 Vec
    let mut stmt = conn
        .prepare("SELECT name FROM prisoner_skill WHERE prisoner_id = ?1")
        .context("prepare select skills failed")?;

    let skill_names: Vec<String> = stmt
        .query_map([prisoner_id], |row| row.get::<_, String>(0))
        .context("query_map skills failed")?
        .collect::<Result<Vec<_>, _>>()
        .context("collect skill names failed")?;

    // 释放查询语句
    drop(stmt);

    // 预编译更新语句
    let mut upd = conn
        .prepare(
            "UPDATE prisoner_skill
             SET level = ?1, experience = ?2
             WHERE prisoner_id = ?3 AND name = ?4",
        )
        .context("prepare update skills failed")?;

    let mut updated = 0usize;

    // 遍历技能名并更新
    for name in skill_names {
        // 只更新白名单里认识的技能，与你的 FromStr 规则保持一致
        if SkillName::from_str(&name).is_err() {
            continue;
        }

        // 执行更新
        let n = upd
            .execute(params![FULL_LEVEL, FULL_EXP, prisoner_id, name])
            .context("execute update skill failed")?;
        updated += n as usize;
    }

    Ok(updated)
}

/// 在 body_sim 二进制 blob 里把给定 key 的 DoubleProperty 值写成 value
fn update_body_sim_double(body_sim: &mut [u8], key: &[u8], value: f64) -> anyhow::Result<()> {
    // 找 key 的起点
    let key_offset = body_sim
        .windows(key.len())
        .position(|w| w == key)
        .with_context(|| format!("属性键未找到: {}", String::from_utf8_lossy(key)))?;

    // 校验属性类型名为 "DoubleProperty"
    let prop_ty_off = key_offset + key.len() + BODY_SIM_KEY_PADDING;
    let prop_ty_end = prop_ty_off + DOUBLE_PROPERTY.len();
    if prop_ty_end > body_sim.len() {
        bail!("属性类型越界: {}", String::from_utf8_lossy(key));
    }
    if &body_sim[prop_ty_off..prop_ty_end] != DOUBLE_PROPERTY {
        bail!(
            "属性 {} 不是 DoubleProperty，实际是: {}",
            String::from_utf8_lossy(key),
            String::from_utf8_lossy(&body_sim[prop_ty_off..prop_ty_end])
        );
    }

    // 计算值偏移并写入 little-endian f64
    let val_off = prop_ty_end + BODY_SIM_VALUE_PADDING;
    let val_end = val_off + std::mem::size_of::<f64>();
    if val_end > body_sim.len() {
        bail!("属性值越界: {}", String::from_utf8_lossy(key));
    }

    body_sim[val_off..val_end].copy_from_slice(&value.to_le_bytes());
    Ok(())
}

/// 设置指定的属性值
fn set_attributes(
    conn: &rusqlite::Connection,
    prisoner_id: i64,
    attributes: &[AttrKV],
) -> anyhow::Result<usize> {
    if attributes.is_empty() {
        return Ok(0);
    }

    // 读出 blob
    let mut body: Vec<u8> = conn
        .query_row(
            "SELECT body_simulation FROM prisoner WHERE id = ?1",
            params![prisoner_id],
            |row| row.get(0),
        )
        .context("读取 body_simulation 失败")?;

    // 逐项更新
    let mut ok_count = 0usize;
    for attr_kv in attributes {
        let key = attr_kv.name.to_string();
        let key_bytes = key.as_bytes();
        
        update_body_sim_double(&mut body, key_bytes, attr_kv.value)
            .with_context(|| format!("写入属性 {} 失败", key))?;
        ok_count += 1;
    }

    // 回写 blob
    conn.execute(
        "UPDATE prisoner SET body_simulation = ?1 WHERE id = ?2",
        params![body, prisoner_id],
    )
    .context("更新 body_simulation 失败")?;

    Ok(ok_count)
}

/// 一键把该囚犯的四个基础属性写满，返回成功更新的数量
pub fn max_out_attributes(conn: &rusqlite::Connection, prisoner_id: i64) -> anyhow::Result<usize> {
    // 读出 blob
    let mut body: Vec<u8> = conn
        .query_row(
            "SELECT body_simulation FROM prisoner WHERE id = ?1",
            params![prisoner_id],
            |row| row.get(0),
        )
        .context("读取 body_simulation 失败")?;

    // 逐项更新
    let mut ok_count = 0usize;
    for (key, val) in TARGET_ATTRS {
        update_body_sim_double(&mut body, key, *val)
            .with_context(|| format!("写入属性 {} 失败", String::from_utf8_lossy(key)))?;
        ok_count += 1;
    }

    // 回写 blob
    conn.execute(
        "UPDATE prisoner SET body_simulation = ?1 WHERE id = ?2",
        params![body, prisoner_id],
    )
    .context("更新 body_simulation 失败")?;

    Ok(ok_count)
}

// ================== 打印：技能 ==================

/// 打印该囚犯的技能（只显示白名单里认识的名字）
pub fn show_skills(conn: &rusqlite::Connection, prisoner_id: i64) -> anyhow::Result<()> {
    let mut stmt = conn.prepare(
        "SELECT name, level, experience
         FROM prisoner_skill
         WHERE prisoner_id = ?1
         ORDER BY name ASC",
    ).context("prepare select skills failed")?;

    struct Row { name: String, level: i64, exp: f64 }
    let rows = stmt.query_map(params![prisoner_id], |r| {
        Ok(Row {
            name: r.get(0)?,
            level: r.get(1)?,
            exp: r.get(2)?,
        })
    }).context("query_map skills failed")?;
    
    let mut table = Table::new();
    table.set_header(vec![
        Cell::new("Skill").fg(Color::Cyan),
        Cell::new("Level").fg(Color::Cyan),
        Cell::new("Experience").fg(Color::Cyan),
    ]);

    let mut shown = 0usize;
    for row in rows {
        let Row { name, level, exp } = row.context("read skill row failed")?;
        // 只显示白名单技能
        if let Ok(parsed) = SkillName::from_str(&name) {
            table.add_row(vec![
                Cell::new(&parsed.to_string()),
                Cell::new(&level.to_string()),
                Cell::new(&format!("{:.0}", exp)),
            ]);
            shown += 1;
        }
    }

    if shown == 0 {
        println!("[no known skills found for this prisoner]");
    } else {
        println!("{}", table);
    }
    Ok(())
}

// ================== 打印：属性 ==================
// 目标属性键名（打印顺序也按这个来）
const TARGET_ATTR_KEYS: &[(&[u8], &str, &str)] = &[
    (b"BaseStrength",     "BaseStrength",     "1.0..=8.0"),
    (b"BaseConstitution", "BaseConstitution", "1.0..=5.0"),
    (b"BaseDexterity",    "BaseDexterity",    "1.0..=5.0"),
    (b"BaseIntelligence", "BaseIntelligence", "1.0..=8.0"),
];

/// 读取 DoubleProperty 的 f64 值；找不到返回 Ok(None)
fn read_body_sim_double(body_sim: &[u8], key: &[u8]) -> anyhow::Result<Option<f64>> {
    // 查找键
    let Some(key_offset) = body_sim.windows(key.len()).position(|w| w == key) else {
        return Ok(None); // 某些版本或损坏存档：缺键时不炸
    };

    // 校验类型名
    let prop_ty_off = key_offset + key.len() + BODY_SIM_KEY_PADDING;
    let prop_ty_end = prop_ty_off + DOUBLE_PROPERTY.len();
    if prop_ty_end > body_sim.len() {
        bail!("属性 {} 类型区域越界", String::from_utf8_lossy(key));
    }
    if &body_sim[prop_ty_off..prop_ty_end] != DOUBLE_PROPERTY {
        bail!(
            "属性 {} 不是 DoubleProperty（实际是 {}）",
            String::from_utf8_lossy(key),
            String::from_utf8_lossy(&body_sim[prop_ty_off..prop_ty_end])
        );
    }

    // 读取值
    let val_off = prop_ty_end + BODY_SIM_VALUE_PADDING;
    let val_end = val_off + std::mem::size_of::<f64>();
    if val_end > body_sim.len() {
        bail!("属性 {} 值区域越界", String::from_utf8_lossy(key));
    }
    let mut buf = [0u8; 8];
    buf.copy_from_slice(&body_sim[val_off..val_end]);
    Ok(Some(f64::from_le_bytes(buf)))
}

/// 打印该囚犯的四个基础属性；缺失时标注 N/A
pub fn show_attributes(conn: &rusqlite::Connection, prisoner_id: i64) -> anyhow::Result<()> {
    let body: Vec<u8> = conn.query_row(
        "SELECT body_simulation FROM prisoner WHERE id = ?1",
        params![prisoner_id],
        |row| row.get(0),
    ).context("读取 body_simulation 失败")?;

    let mut table = Table::new();
    table.set_header(vec![
        Cell::new("Attribute").fg(Color::Cyan),
        Cell::new("Value").fg(Color::Cyan),
        Cell::new("Range").fg(Color::Cyan),
    ]);

    for (key_bytes, display, range) in TARGET_ATTR_KEYS {
        match read_body_sim_double(&body, key_bytes)? {
            Some(v) => {
                table.add_row(vec![
                    Cell::new(display),
                    Cell::new(&format!("{:.4}", v)),
                    Cell::new(range),
                ]);
            }
            None => {
                table.add_row(vec![
                    Cell::new(display),
                    Cell::new("N/A").fg(Color::Red),
                    Cell::new(range),
                ]);
            }
        }
    }
    
    println!("{}", table);
    Ok(())
}

